<?php
/**
 * @author Sunny
 * @date 2023/7/24
 * @time 14:39 下午
 */

namespace app\api\service;
use think\facade\Db;
use DOMDocument;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Response;

class ArticlePushService{
    const successState = 3;//发布成功后article里的state状态码
    const errorState = 4;//发布失败后article里的state状态码    
    public $article_id;//要发布的文章id
    public $site_id;//目标站点id
    public $site_cat_id;//目标站点栏目id
    public $insert_picture_class;//插入文章中图片的分类id
    public $insert_picture_line;//在第n段前插入图片：0为随机
    public $relation_article_num;//关联文章数量
    public $relation_article_str;//关联文章文字(默认：相关文章)
    public $article;//文章信息
    public $site;//站点信息
    public $site_link;//站点锚链接信息
    //
    public $picture;//文章配置 根据$insert_picture_class提取的文章图片
    //发布参数设置
    public function __construct($options)
    {
        $this->article_id = $options['article_id'];
        $this->site_id = $options['site_id'];
        $this->site_cat_id = $options['site_cat_id'];
        $this->insert_picture_class = $options['insert_picture_class'];
        $this->insert_picture_line = $options['insert_picture_line'];
        //如果relation_article_num是数组，证明是一个区间，则在这个区间里生成一个随机数
        if(is_array($options['relation_article_num'])){
            $options['relation_article_num']=rand($options['relation_article_num'][0],$options['relation_article_num'][1]);
        }
        $this->relation_article_num = $options['relation_article_num'];
        $this->relation_article_str = $options['relation_article_str']?$options['relation_article_str']:"<h3>更多和<b>[keyword]</b>相关的文章</h3>";

    }
    //查询文章
    public function getArticle(){
        $article=\app\common\model\Article::find($this->article_id);
        $article_data= \app\common\model\ArticleContent::find($this->article_id)->toArray();
        $article['content']=$article_data['name'];
        $this->article=$article;
        return $article;
    }
    //查询站点
    public function getSite(){
        $this->site=\app\common\model\Site::find($this->site_id);
    }
    //查询站点锚链接
    public function getSiteLink(){
        $this->site_link=\app\common\model\SiteLink::where("site_id","=",$this->site_id)->orderRaw('CHAR_LENGTH(name) DESC')->select();
    }
    //向文章中插入图片
    public function insertArticlePicture(){
        $img = \app\common\model\MemberFile::order('update_time', 'asc')->where('img_class_id', $this->insert_picture_class)->find();
        if ($img) {
            $this->picture=$img['filepath'];
            $this->article['content'] = $this->insertImageBeforeNthParagraph($this->article['content'], $this->insert_picture_line, $img['filepath'], $this->article['name']);
            $img->force()->save();//修改图片，以更新update_time字段
            return $this->article['content'];
        }
    }
    //向指定段落后插入图片
    private function insertImageBeforeNthParagraph($html, $n, $imageSrc, $alt) {
        $dom = new DOMDocument();
        libxml_use_internal_errors(true);
        $dom->loadHTML('<div>' . mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')  . '</div>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
        $paragraphs = $dom->getElementsByTagName('p');
        $paragraphCount = $paragraphs->length;
        if ($n == 0) {
            // Randomly choose a position between 1 and the number of <p> tags
            $randomPosition = rand(1, $paragraphCount);
            $targetParagraph = $paragraphs->item($randomPosition - 1);
        } elseif ($n > $paragraphCount) {
            // Insert at the end if $n is greater than the number of <p> tags
            $targetParagraph = $dom->getElementsByTagName('div')->item(0);
        } else {
            // Insert before the nth <p> tag
            $targetParagraph = $paragraphs->item($n - 1);
        }
        // 创建<img>元素并设置属性
        $newImage = $dom->createElement('img');
        $newImage->setAttribute('src', $imageSrc);
        $newImage->setAttribute('alt', $alt);

        // 创建<p>元素并设置样式
        $newParagraph = $dom->createElement('p');
        $newParagraph->setAttribute('style', 'text-align:center');
        $newParagraph->appendChild($newImage);
        
        $parentNode = $targetParagraph->parentNode;
        $parentNode->insertBefore($newParagraph, $targetParagraph);        

        // Get the inner HTML of the modified <div> element
        $modifiedHtml = '';
        foreach ($dom->getElementsByTagName('div')->item(0)->childNodes as $node) {
            $modifiedHtml .= $dom->saveHTML($node);
        }
        return $modifiedHtml;
    }

    //设置相关文章
    public function setRelationArticle(){
        $keywords=explode(',',$this->article['keywords']);
        $keywords = array_filter($keywords);
        $relation_article_str_keywords="";//设置相关文章标题中包含主关键词
        if($keywords){
            //设置数组排序，从长到短
//            usort($keywords, function ($a, $b) {
//                return strlen($b) - strlen($a);
//            });   
            //对数组随机排序，会增加相关文章的多样性
            shuffle($keywords);
            //
            foreach($keywords as $key=>$val){
                $data=\app\common\model\Article::field('name,article_url')
                    ->where([
                    ['article_class_id','=',$this->article['article_class_id']],
                    ['state','=',3],
                    ['site_id','=',$this->site_id],
                    ['name','like',"%".$val."%"],
                ])->whereNotNull('article_url')->order("push_time desc")->limit($this->relation_article_num)->select()->toArray();
                if($data){
                    $relation_article_str_keywords=$val;
                    break;
                }
                //这里设置多个模糊查询，便于没有匹配到文章的时候在后面做更多匹配
                $keywords[$key]='%'.$val.'%';
            }
            //如果没有匹配到，第一个关键词就做为$relation_article_str_keywords然后查询所有关键词
            if(!$data){
                $relation_article_str_keywords=$keywords[0];
                $data=\app\common\model\Article::field('name,article_url')
                    ->where([
                    ['article_class_id','=',$this->article['article_class_id']],
                    ['state','=',3],
                    ['site_id','=',$this->site_id],
                    ['name','like',$keywords,'OR'],
                ])->whereNotNull('article_url')->order("push_time desc")->limit($this->relation_article_num)->select()->toArray();
            }

            $str="";
            if($data){
                $str="<h3>".$this->relation_article_str."</h3><ul>";
                if($relation_article_str_keywords){
                    $str= str_replace("[keyword]",$relation_article_str_keywords,$str);//这里是兼容之前keyword写法错误
                    $str= str_replace("[keywords]",$relation_article_str_keywords,$str);
                }
                foreach($data as $v){
                    $title= str_replace($relation_article_str_keywords, "<b>".$relation_article_str_keywords."</b>", $v['name']);
                    $str.='<li><a href="'.$v['article_url'].'" target="_blank">'.$title.'</a></li>';
                }
                $str.="</ul>";
            }

            $this->article['content']=$this->article['content'].$str;
        }
    }
    //根据站点关键词，替换文章里的keywords
    public function repArticleKeywords(){
        $keywords_library=trim($this->site['keywords_library']);
        $keywords_library=explode(",",$keywords_library);
        $keywords_library=array_filter($keywords_library);
        if($keywords_library){
            //随机打乱数组顺序
            shuffle($keywords_library);
            $new_keywords=[];
            foreach($keywords_library as $str){
                //查询文章中是否包含这个关键词
                if(strstr($this->article['content'],$str)){
                    $new_keywords[]=$str;
                }
                //是否等于随机的3-6之间
                if(count($new_keywords)==rand(3,6)){
                    break;
                }
            }
            if($new_keywords){
                $this->article['keywords'] = implode(", ", $new_keywords);
            }
            return;
        }
    }
    //文章关键字链接替换
    public function repSiteLink(){
        if($this->site_link){
            foreach ($this->site_link as $link) {
                //使用repALinkTemp函数替换，之前的替换方法有bug，比如替换fencing关键字，如果a链接里也包含fencing就有问题了
                $this->article['content'] =$this->repALinkTemp($link['name'], '<a href="'.$link['url'].'" target="_blank">' . $link['name'] . '</a>', $this->article['content']);
            }
        }
    }
    //这个替换函数是先将$content先拿出来，替换后再放回去
    private function repALinkTemp($str,$toStr,$content,$num=1){
        $tempLinks = array();
        //选提取a标签暂存一下
        $pattern = '/<a[^>]*>.*?<\/a>/';
        preg_match_all($pattern, $content, $matches);
        $tempLinks = $matches[0];
        $placeholder = '###TEMPLINK###';
        $htmlWithPlaceholders = preg_replace($pattern, $placeholder, $content);
        //开始替换
        $htmlWithPlaceholders = preg_replace('/(?<!href=")(?<!alt=")' .$str. '(?!<\/a>)(?<!\/>)/',$toStr, $htmlWithPlaceholders,$num);
        //恢复标签
        foreach ($tempLinks as $tempLink) {
            $htmlWithPlaceholders = preg_replace('/' . preg_quote($placeholder) . '/', $tempLink, $htmlWithPlaceholders, 1);
        }
        return $htmlWithPlaceholders;
    }
    
    //获取发布的数据格式
    public function getPushFormData()
    {

        $form_data = [];
        $form_data[$this->site['column_field']] = $this->site_cat_id;
        $form_data[$this->site['article_content_field']] = $this->article['content'];
        //缩略图
        if($this->site['thumbnail_field']&&$this->picture){
            $form_data[$this->site['thumbnail_field']] = $this->picture;
        }
        //发布时间
        if($this->site['create_time_field']){
            $form_data[$this->site['create_time_field']] = date('Y-m-d H:i:s', time());
        }
        //tag字段
        if($this->site['tags_field']){
            $form_data[$this->site['tags_field']] = $this->article['keywords'];
        }
        //keywords
        if($this->site['keywords_field']){
            $form_data[$this->site['keywords_field']] = $this->article['keywords'];
        } 
        //description_field
        if($this->site['description_field']){
            $form_data[$this->site['description_field']] = $this->getDescription($this->site['description_length']);
        }        
        //标题字段
        $form_data[$this->site['title_field']] = $this->article['name'];
        //key字段
        $form_data['key'] = $this->site['secret_key'];
        //
        $url = $this->site['website'] . $this->site['publishing_interface'];
        $split_flag = '?';
        if(strpos($url,'?')!==false){
            $split_flag = '&';
        }
        $url = ($this->site['is_https'] ? 'https://' : 'http://') . $url .$split_flag  .'key=' . $this->site['secret_key'];

        if ($this->site['other_fields']) {
            $other_fields = explode("\n", $this->site['other_fields']);
            foreach ($other_fields as $field) {
                $field = trim($field, "\r");
                if (strstr($field, ':')) {
                    [$key, $val] = explode(':', $field);
                    //替换相关标签
                    $val=$this->repPushOtherLib($val);                    
                    $form_data[$key] = $val;
                }
            }
        }

        $cookies = [];
        if ($this->site['cookies']) {
            $other_fields = explode("\n", $this->site['cookies']);
            foreach ($other_fields as $field) {
                if (strstr($field, ':')) {
                    [$key, $val] = explode(':', $field);
                    $cookies[$key] = $val;
                }
            }
        }
        $headers = [];
        if ($this->site['headers']) {
            $other_fields = explode("\n", $this->site['headers']);
            foreach ($other_fields as $field) {
                if (strstr($field, ':')) {
                    [$key, $val] = explode(':', $field);
                    $headers[$key] = $val;
                }
            }
        }

        return [
            'data' => $form_data,
            'cookies' => $cookies,
            'headers' => $headers,
            'url' => $url,
            'flag' => $this->site['publish_success_flag'],
        ];
    }  
    //提取文章简介
    private function getDescription($length=200) {
        return trim(mb_substr(strip_tags($this->article['content']),0,$length,'utf-8'));
    }
    //替换发布模块其它字段相关标签
    private function repPushOtherLib($val){
        return str_replace(["[time]",], [time(),], $val);
    }    
    /**
     * 发送数据
     * @Query("$url":提交的url)
     * @Query("$headers":header)
     * @Query("$cookies":cookies)
     * @Query("$data":post数据)
     * @Query("$flag":返回成功的标志)
     * @Returned("status"：提交状态，1为提交成功，0为程序返回错误)
     * @Returned("msg", 返回的内容)
     * @Returned("article_url", 发布成功后的url")
     */    
    public function push($option=["url"=>"","headers"=>[],"cookies"=>[],"data"=>[],"flag"=>""]){
        $url=$option['url'];
        $headers=$option['headers'];
        $cookies=$option['cookies'];
        $data=$option['data'];
        $flag=$option['flag'];
        $client = new Client([
            'timeout' => 800,
            'verify' => false,
        ]);

        try {
            // 发送请求
            $response = $client->post($url, [
                'headers' => $headers,
                'cookies' => $cookies,
                'form_params' => $data,
            ]);
            $statusCode = $response->getStatusCode();
            $headers = $response->getHeaders();
            $content = $response->getBody()->getContents();
            $content = preg_replace('/^\xEF\xBB\xBF/', '', $content);//去掉文件bom
            $contentArr=json_decode($content,true);

            $article_url="";
            //是json格式
            if($contentArr){
                if(isset($contentArr['url'])){
                    $status=$contentArr['status']==1?1:0;
                }else{
                    $status=0;
                }
                if(isset($contentArr['msg'])){
                    $msg=$contentArr['msg']?$contentArr['msg']:$content;
                }else{
                    $msg="";
                }
                if(isset($contentArr['url'])){
                    $article_url=$contentArr['url'];
                    //如果有返回链接，但是链接中没有包含http则需要加上网址
                    if($article_url&&strstr($article_url, "http")==false){
                        //从接口url中提取网址部分
                        preg_match('/^(https?:\/\/[^\/]+).*$/i', $url, $matches);
                        //判断返回的$article_url是否以/开头
                        if (preg_match('/^\/.*$/', $article_url)) {
                            $article_url= $matches[1].$article_url;
                        } else {
                            $article_url= $matches[1]."/".$article_url;
                        }
                    }
                }

            }else{
                //非json格式（这里主要是兼容老的接口，老的接口返回的就是一个字符串类似于：发布成功）
                //验证返回的内容是否有包含发布成功的标志
                if (strstr($content, $flag)) {
                    $status=1;
                    $msg=$content;
                    $article_url="";
                }else{
                    $status=0;
                    $msg='返回值无成功标志。返回代码：'.$content;
                    $article_url="";
                }
            }
            return [
                'status'=>$status,
                'msg' => $msg,
                'headers'=>$headers,
                'article_url' => $article_url,
            ];            
        } catch (RequestException $e) {
            if ($e->hasResponse()) {
                $statusCode = $e->getResponse()->getStatusCode();
                $content = $e->getResponse()->getBody()->getContents();
            } else {
                $statusCode = $e->getCode();
                $content = $e->getMessage();
            }
            return [
                'status'=>0,
                'msg' => $content,
                'headers'=>$headers,
                'article_url' => "",
            ];            
        }
    }
    //修改文章发布状态
    public function updateArticle($state,$article_url=""){
        $data['id']=$this->article_id;
        $data['push_time']=time();
        $data['state']=$state;
        $data['site_id']=$this->site_id;
        if($article_url){
            $data['article_url']=$article_url;
        }
        Db::name("article")->update($data);
    }
    //增加发布日志
    public function addPushLog($state,$remarks="",$push_plan_id=0){
        $data['state']=$state;
        $data['member_id']=$this->article['member_id'];
        $data['article_id']=$this->article_id;
        $data['push_plan_id']=$push_plan_id;
        $data['site_id']=$this->site_id;
        $data['site_catid']=$this->site_cat_id;
        if($remarks){
            $data['remarks']=$remarks;
        }
        \app\common\model\PushLog::create($data);
    }
}
